<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
<script>
    // 1.可以使用 Array.isArray() 方法。这个方法会在给定值是数组时返回 true，否则返回 false。例如：

    // const arr = [1, 2, 3];
    // console.log(Array.isArray(arr)); // 输出: true

    // const obj = { a: 1, b: 2 };
    // console.log(Array.isArray(obj)); // 输出: false

    // const str = 'hello';
    // console.log(Array.isArray(str)); // 输出: false


    // 2.instanceof 操作符：可以使用 instanceof 操作符检查一个变量是否是特定类型的实例，对于数组来说，可以将其和 Array 构造函数结合使用。例如：
    // const arr = [1, 2, 3];
    // console.log(arr instanceof Array); // 输出: true

    // const obj = { a: 1, b: 2 };
    // console.log(obj instanceof Array); // 输出: false

    // const str = 'hello';
    // console.log(str instanceof Array); // 输出: false

    /*
        缺点
        （1）
        instanceof检查的是一个对象的原型链上是否存在某个构造函数的prototype属性。
        而Array.isArray()是专门用来检查一个值是否是数组的，它不会受到原型链的影响。
        // 创建一个数组
            let arr = [1, 2, 3];

            // 通过 Array.prototype 创建一个对象
            let obj = Object.create(Array.prototype);
            obj.push(4);

            // 使用 instanceof
            console.log(arr instanceof Array); // true
            console.log(obj instanceof Array); // true

            // 使用 Array.isArray
            console.log(Array.isArray(arr)); // true
            console.log(Array.isArray(obj)); // false

        在上面的代码中，arr是一个标准的数组，而obj是一个通过Array.prototype创建的对象。
        尽管obj的原型链上包含了Array.prototype，但是Array.isArray(obj)返回的是false，
        因为Array.isArray()只会检查该对象是否是一个真正的数组，而instanceof检查的是对象的原型链。
        尽管obj继承自Array.prototype，但它并不是一个通过Array构造函数创建的实例。
        （2）
            跨 iframe 的问题
            在不同的执行环境（如不同的iframe或窗口）中，如果你使用instanceof来检测数组，
            可能会遇到问题。这是因为不同的环境可能会创建出不同的Array构造函数，
            而instanceof依赖于对象的原型链，而这个链可能会指向不同的构造函数。
            // 在不同的 iframe 或 window 上，可能会得到不同的 Array 构造函数
            let iframeWindow = window.frames[0];
            iframeWindow.eval('let arr = [1, 2, 3];');

            // 在父窗口
            console.log(arr instanceof Array);  // true
            console.log(iframeWindow.arr instanceof Array);  // false (因为 Array 构造函数不同)

            在不同的执行环境（如不同的window或iframe）中，如果Array构造函数不同，
            那么instanceof可能会导致不正确的结果。而Array.isArray()不受这些环境的影响，它只检查该对象是否为Array类型。
    */

    // 3.检查对象原型：可以通过检查对象的原型链来确定一个变量是否是数组。例如：

    // const arr = [1, 2, 3];
    // console.log(arr.constructor === Array); // 输出: true

    // const obj = { a: 1, b: 2 };
    // console.log(obj.constructor === Array); // 输出: false

    // const str = 'hello';
    // console.log(str.constructor === Array); // 输出: false


    /*
        缺点
            1. 跨执行环境的问题（例如跨 iframe）
                在不同的执行环境（如不同的 iframe 或 window）中，每个环境都可能有自己的 Array 构造函数。
                因此，通过 constructor 来判断数组时，如果对象是跨执行环境（如跨 iframe）传递的，可能会导致错误的结果。
                // 在主窗口中创建一个数组
                let arr = [1, 2, 3];

                // 假设 arr 是传递到另一个 iframe 的对象，iframe 中有另一个不同的 window 环境
                let iframeWindow = window.frames[0];
                iframeWindow.eval('let arr2 = [4, 5, 6];');

                // 通过 constructor 判断
                console.log(arr.constructor === Array);  // true
                console.log(iframeWindow.arr2.constructor === Array);  // false
            2. constructor 可能被修改
                constructor 属性可以被修改，因此如果在代码中修改了某个对象的 constructor，
                那么通过 constructor 判断数组类型时可能会出错。
                let arr = [1, 2, 3];
                arr.constructor = function() {}; // 修改 constructor
                console.log(arr.constructor === Array);  // false
                如果 constructor 被人为修改，那么即使 arr 是一个数组，arr.constructor === Array 可能会返回 false
    
    */

    //4.使用 Array.prototype 方法：可以利用数组的原型方法来判断一个变量是否是数组。例如：
    // const arr = [1, 2, 3];
    // console.log(Array.prototype.isPrototypeOf(arr)); // 输出: true

    // const obj = { a: 1, b: 2 };
    // console.log(Array.prototype.isPrototypeOf(obj)); // 输出: false

    // const str = 'hello';
    // console.log(Array.prototype.isPrototypeOf(str)); // 输出: false

    //5.toString 方法：可以使用 toString 方法将变量转换为字符串，然后检查字符串是否以 "[object Array]" 开头，以确定变量是否为数组。例如：
    // const arr = [1, 2, 3];
    // console.log(Object.prototype.toString.call(arr) === '[object Array]'); // 输出: true

    // const obj = { a: 1, b: 2 };
    // console.log(Object.prototype.toString.call(obj) === '[object Array]'); // 输出: false

    // const str = 'hello';
    // console.log(Object.prototype.toString.call(str) === '[object Array]'); // 输出: false

    /*
            缺点
                无法判断类数组对象
                Object.prototype.toString.call() 主要用于判断对象的类型，
                而对于类数组对象（如 arguments 对象、NodeList 等），
                它无法直接判断。对于这类对象，你可能需要使用 Array.isArray() 或其它方法来判断。
        function test() {
            let args = arguments;
            console.log(Object.prototype.toString.call(args)); // "[object Object]" 
            console.log(Array.isArray(args)); // false
        }

        test(1, 2, 3);

    */

    // 6.检查数组的长度和索引属性：可以检查数组对象是否具有 length 属性以及数字索引的属性，以确定其是否为数组。例如：
    // const arr = [1, 2, 3];
    // console.log(Array.isArray(arr) && arr.length !== undefined && arr[0] !== undefined); // 输出: true

    // const obj = { a: 1, b: 2 };
    // console.log(Array.isArray(obj) && obj.length !== undefined && obj[0] !== undefined); // 输出: false

    // const str = 'hello';
    // console.log(Array.isArray(str) && str.length !== undefined && str[0] !== undefined); // 输出: false

</script>
</html>